﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpMUD.Framework.Services.Net;
using SharpMUD.Framework.Workflow.Generic;
using SharpMUD.Framework.Net;
using SharpMUD.Framework.Net.Telnet;
using SharpMUD.Framework.CorlibExtensions;
using System.Threading;

namespace SharpMUD.Framework.Services.Telnet
{
	public partial class TelnetServer
        : INetworkService
    {
        #region Sessions
        private List<TelnetSession> _Sessions = new List<TelnetSession>();
        private List<INetworkConnectionFilterService<TelnetSession>> _DetectedFilterServices = new List<INetworkConnectionFilterService<TelnetSession>>();
        #endregion

        public IEnumerable<INetworkConnection> Connections
        {
            get
            {
                return (IEnumerable<INetworkConnection>)_Sessions;
            }
        }

        public IEnumerable<INetworkConnectionListener> ConnectionListeners
        {
            get
            {
                return _ConnectionListeners;
            }
        }

        #region Listeners
        private List<INetworkConnectionListener> _ConnectionListeners;

        void ConnectionListener_ConnectionAttempted(INetworkConnectionListener sender, INetworkConnection connection)
        {
            if (connection is TelnetSession)
            {
                // Connection filtering
                Nullable<bool> filtersApprove = null;
                //var topLevelFilterServices = this._DetectedFilterServices.ToList();
                var topLevelFilterServices = this.ServicesHost.Services.Where(s => s is INetworkConnectionFilterService<TelnetSession>).Select(s => s as INetworkConnectionFilterService<TelnetSession>);
                foreach(var fs in topLevelFilterServices)
                {
                    var currentFilterApproves = fs.ApproveConnectionAttempt(connection as TelnetSession);
                    if (currentFilterApproves.HasValue)
                    {
                        filtersApprove = currentFilterApproves;
                        break;
                    }
                }

                sender.RemoveWaitingConnection(connection, filtersApprove.HasValue ? filtersApprove.Value : true);
                if (!filtersApprove.HasValue || filtersApprove.Value == true)
                {
                    _Sessions.Add(connection as TelnetSession);
                    (connection as TelnetSession).ConnectionTerminated += new EventHandler(TelnetSession_ConnectionTerminated);
                    (connection as TelnetSession).Start();
                }
            }
            else
                throw new NotImplementedException();
        }

        void TelnetSession_ConnectionTerminated(object sender, EventArgs e)
        {
            lock (this._Sessions)
            {
                this._Sessions.Remove(sender as TelnetSession);
                (sender as TelnetSession).Dispose();
            }
        }

        private void SetupListeners()
        {
            if (_ConnectionListeners != null)
                throw new InvalidOperationException();
            log.Info("Configuring listeners...");
            _ConnectionListeners = new List<INetworkConnectionListener>();
            _ConnectionListeners.AddRange(
                (
                from p in this.Configuration.Ports
                select new SocketReturningConnectionListener((s)=>new TelnetSession(s,this),p)
                ).Cast<INetworkConnectionListener>()
                        );
            foreach (var cl in _ConnectionListeners)
            {
                cl.ConnectionAttempted += new ConnectionListenerEventHandler(ConnectionListener_ConnectionAttempted);
            }
            log.Info("Listeners configured.");
        }

        private void StartListeners()
        {
            if (this._ConnectionListeners == null)
                throw new InvalidOperationException();
            log.Info("Starting listeners...");
            foreach (var cl in _ConnectionListeners)
            {
                cl.Start();
            }
            log.Info("Listeners started.");
        }

        private void StopListeners()
        {
            if (this._ConnectionListeners == null)
                throw new InvalidOperationException();
            log.Info("Stopping listeners...");
            foreach (var cl in _ConnectionListeners)
            {
                cl.Stop();
            }
            log.Info("Listeners stopped.");
        }

        private void StopSessions()
        {
            if (this._Sessions == null)
                throw new InvalidOperationException();
            log.Info("Stopping sessions...");
            foreach (var session in _Sessions)
            {
                session.Stop();
            }
            log.Info("Sessions stopped.");
        }

        private void DisposeListeners()
        {
            if (this._ConnectionListeners != null)
            {
                foreach (var cl in _ConnectionListeners)
                {
                    try
                    {
                        cl.Stop();
                    }
                    catch (Exception e)
                    {
                        // do nothing
                    }
                    finally
                    {
                        if (cl is IDisposable)
                            (cl as IDisposable).Dispose();
                    }
                }
                _ConnectionListeners.Clear();
                _ConnectionListeners = null;
            }
        }

        private void DisposeSessions()
        {
            if (this._Sessions != null)
            {
                foreach (var session in this._Sessions)
                {
                    try
                    {
                        session.Stop();
                    }
                    catch (Exception e)
                    {
                        // do nothing
                    }
                    finally
                    {
                        if (session is IDisposable)
                            (session as IDisposable).Dispose();
                    }
                }
                this._Sessions.Clear();
                this._Sessions = null;
            }
        }

        private void PulseListeners()
        {
            if (this._ConnectionListeners == null)
                throw new InvalidOperationException();
            
            var t = _TaskFactory.StartNew(() =>
            {
                foreach (var cl in _ConnectionListeners.OfType<IPulseDependent>())
                {
                    cl.Pulse();
                }
            }, this._PulseCancellationTokenSource.Token);
            try
            {
                t.Wait(this._PulseCancellationTokenSource.Token);
            }
            catch (OperationCanceledException e)
            {
                // Do nothing
            }
        }
        #endregion

        private void PulseSessions()
        {
            if (this._Sessions == null)
                throw new InvalidOperationException();

            var t = _TaskFactory.StartNew(() =>
            {
                foreach (var session in _Sessions.OfType<IPulseDependent>())
                {
                    var innerT = _TaskFactory.StartNew(
                        (s)=>(s as TelnetSession).Pulse(),
                        session,
                        this._PulseCancellationTokenSource.Token,
                        System.Threading.Tasks.TaskCreationOptions.AttachedToParent
                        | System.Threading.Tasks.TaskCreationOptions.PreferFairness,
                        System.Threading.Tasks.TaskScheduler.Current
                        );
                }
                
            }, this._PulseCancellationTokenSource.Token);
            try
            {
                t.Wait(this._PulseCancellationTokenSource.Token);
            }
            catch (OperationCanceledException e)
            {
                // Do nothing
            }
        }
	}
}
